﻿using AIClient.Configs;
using AIClient.Utils;
using Extensions;
using NetMsg.Card;
using NetMsg.CardAction;
using NetWorkMsg;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using NFX;

namespace AIClient.Players.Module.Card
{
    public partial class BattleField
    {
        #region 常量定义

        private const int _handFillUpNum = 4; // 认为手卡满时手卡数
        private const int _crystalHpMax  = 3; // 水晶生命Max
        private const int _crystalNumber = 3; // 水晶数量
        private const int _rowNum        = 2; // 行数(每列卡牌数)
        private const int _colNum        = 3; // 列数(每行卡牌数1)

        #endregion

        private readonly int    _battleId;
        private readonly Player _player;
        private readonly long   _offensive;

        private readonly BehaviourTree      _behaviourTree;
        private          CardBattleFieldCfg _aiCfg => ConfigManager.CardBattleFieldCfg;
        private          Timer              _aiTimer; // AI Timer
        private          Timer              _lifeTimer;
        private          int                _turnCount;    // 回合数,每次收到OnBeginTurn时自增
        private          bool               _isSelfTurn;   // 是否是己方回合
        private          int                _endTurnStamp; // 当前回合结束时间戳

        // 敌人数据
        private          int         _handsCountEnemy; // 手卡数
        private          int         _apNumEnemy;      // 当前可用行动点
        private readonly List<int>   _crystalEnemy;    // 水晶生命
        private readonly BoardCard[] _boardEnemy;

        // 我方数据
        private          List<int>   _deck;        // 卡库
        private          List<int>   _hands;       // 手卡
        private          int         _apNumSelf;   // 可用行动点
        private readonly List<int>   _crystalSelf; // 水晶生命
        private readonly BoardCard[] _boardSelf;


        public BattleField(Player player, ActionEnterSummaryP battlefieldInfo)
        {
            _player    = player;
            _battleId  = battlefieldInfo.Info.BattleId;
            _offensive = battlefieldInfo.Info.Offensive;

            _turnCount       = 0;
            _isSelfTurn      = false;
            _endTurnStamp    = 0;
            _handsCountEnemy = 0;
            _apNumEnemy      = 0;
            _apNumSelf       = 0;
            _crystalEnemy    = (from i in Enumerable.Range(0, _crystalNumber) select _crystalHpMax).ToList();
            _crystalSelf     = (from i in Enumerable.Range(0, _crystalNumber) select _crystalHpMax).ToList();
            _boardSelf       = new BoardCard[_rowNum * _colNum];
            _boardEnemy      = new BoardCard[_rowNum * _colNum];
            _behaviourTree   = CreateCardBehaviourTree();
            _lifeTimer = new Timer((_) => Surrender()
                                 , null
                                 , (new TimeSpan(0, 0, _aiCfg.AiLife)).TotalMilliseconds.AsInt()
                                 , Timeout.Infinite);
            ClientOpEnd();
        }

        public BattleField(Player player, ActionEnterDetailP actionEnterDetail)
        {
            _player = player;
            var battlefieldInfo = actionEnterDetail.Info;
            _battleId  = battlefieldInfo.BattleId;
            _offensive = battlefieldInfo.Offensive;

            _turnCount       = 0;
            _isSelfTurn      = false;
            _endTurnStamp    = 0;
            _handsCountEnemy = 0;
            _apNumEnemy      = battlefieldInfo.Opponent.Stamina;
            _apNumSelf       = battlefieldInfo.Self.Stamina;
            _crystalEnemy    = battlefieldInfo.Opponent.Crystal;
            _crystalSelf     = battlefieldInfo.Self.Crystal;
            _deck            = battlefieldInfo.Self.Deck;
            _hands           = battlefieldInfo.Self.Hand;
            _boardSelf       = new BoardCard[_rowNum * _colNum];
            _boardEnemy      = new BoardCard[_rowNum * _colNum];
            InitializeBoard(_boardSelf, battlefieldInfo.Self.Board);
            InitializeBoard(_boardEnemy, battlefieldInfo.Opponent.Board);

            _behaviourTree = CreateCardBehaviourTree();

            void InitializeBoard(BoardCard[] board, List<int> cards)
            {
                for (var i = 0; i < board.Length; ++i)
                {
                    if (cards[i] != 0)
                        board[i] = new BoardCard(cards[i]);
                }
            }

            ClientOpEnd();
        }

        public void Surrender()
        {
            _player.Card.Surrender(_battleId);
        }

        public void Clear()
        {
            StopBattleThinking();
            _aiTimer?.Dispose();
            _lifeTimer?.Dispose();
        }

        #region 辅助函数

        // 行动者是否是自己?
        private bool IsDoerSelf(bool isOffensive)
        {
            return isOffensive == (_offensive == _player.PlayerId);
        }

        private BoardCard[] GetBoard(bool isOffsensive)
        {
            return IsDoerSelf(isOffsensive) ? _boardSelf : _boardEnemy;
        }

        private List<int> GetCrystal(bool isOffsensive)
        {
            return IsDoerSelf(isOffsensive) ? _crystalSelf : _crystalEnemy;
        }

        // 返回在当前board和ap条件下可上场的手牌
        private List<int> GetCanPutHands()
        {
            // 卡满
            if (_boardSelf.All(boardCard => boardCard          != null)
             && _boardSelf.Any(boardCard => boardCard.CardType == CardType.Guard))
                return _hands.FindAll(hand => GetCardCfg(hand).CardType == CardType.Guard
                                           && GetCardCfg(hand).Power    <= _apNumSelf);

            return _hands.FindAll(hand => GetCardCfg(hand).Power <= _apNumSelf);
        }

        private CardCfg GetCardCfg(int cardId)
        {
            return ConfigManager.CardCfgs[cardId];
        }

        // 不带空的boardcard [[0,2,4][1,3,5]]
        private List<BoardCard> GetColCards(int index, bool isSelf = true)
        {
            var list  = new List<BoardCard>();
            var board = isSelf ? _boardSelf : _boardEnemy;
            for (var i = 0; i < _rowNum; i++)
            {
                var card = board[index * _rowNum + i];
                if (card == null) continue;

                list.Add(card);
            }

            return list;
        }

        private List<BoardCard> GetRowCards(int index, bool isSelf = true)
        {
            var list  = new List<BoardCard>();
            var board = isSelf ? _boardSelf : _boardEnemy;
            for (var i = 0; i < _colNum; i++)
            {
                var card = board[index + _rowNum * i];
                if (card == null) continue;

                list.Add(card);
            }

            return list;
        }

        // 手动技能指定参数
        private int GetAppointTargetArg(bool isTargetSelf)
        {
            if (isTargetSelf)
                return _offensive == _player.PlayerId ? 0 : 1;

            return _offensive != _player.PlayerId ? 0 : 1;
        }

        #endregion

        // 另起一个线程,不然会阻塞消息派发,AI只能读取数据,所有数据在消息中修改
        private void StartBattleThinking()
        {
            if (!_isSelfTurn) return;

            _aiTimer = _aiTimer ?? new Timer(BtUpdate, null, Timeout.Infinite, _aiCfg.AiPeriod);
            _aiTimer.Change(0, _aiCfg.AiPeriod);

            void BtUpdate(object _)
            {
#if DEBUG
                if (_player.Gate.IsInDispatch)
                    throw new Exception("在消息派发时调度了");
                
                var result = _behaviourTree.Update();
                
                if (_player.Gate.IsInDispatch)
                    throw new Exception("在消息派发时调度了");
                
                var color  = Console.ForegroundColor;
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine($"行为树执行结果:{result}");
                if (result == NodeState.Failed)
                {
                    EndFightTurn();
                    Console.WriteLine("我方手牌:");
                    foreach (var hand in _hands)
                    {
                        var cfg = GetCardCfg(hand);
                        Console.WriteLine($"Id:{cfg.CardId} Type:{cfg.CardType} Power:{cfg.Power} Attack:{cfg.Attack} ");
                    }
                }

                Console.ForegroundColor = color;
#else
                if (_behaviourTree.Update() == NodeState.Failed)
                    EndFightTurn();
#endif
            }
        }

        private void StopBattleThinking()
        {
            _aiTimer?.Change(Timeout.Infinite, _aiCfg.AiPeriod);
        }

        // 行为树每次Update只允许产生一个操作,每次进行一个操作后,休息一会等待数据变化
        private void RestBattleThinking()
        {
            StopBattleThinking();
            //var leftTime = _endTurnStamp - Manager.Instance.TimeStamp;
            //var relaxTime = Manager.Instance.Random.Next(_aiCfg.RandWaitSec[_turnCount]) - _aiCfg.AiPeriod;
            //if (relaxTime > leftTime)
            //    relaxTime = leftTime;
#if DEBUG
            var relaxTime = _aiCfg.AiPeriod;
            Console.WriteLine($"Ai休息,将在:{_aiCfg.AiPeriod}后重新调度");
#endif
            _aiTimer.Change(relaxTime, _aiCfg.AiPeriod);
        }

        // 通知服务器客户端操作完成
        private void ClientOpEnd()
        {
            _player.Gate.Send(new ClientOpEndQ {BattleId = _battleId});
        }

        // 通知服务器选卡
        private void PickEnd(List<int> picks, List<int> decks)
        {
            _player.Gate.Send(new PickUpQ {BattleId = _battleId, Picks = picks, Deck = decks});
        }

        // 结束己方回合
        private void EndFightTurn()
        {
            if (_endTurnStamp <= Manager.Instance.TimeStamp) return;

            _player.Gate.Send(new EndFightTurnQ {BattleId = _battleId});
        }

        // 放置卡牌
        private void PutCard()
        {
            if (_endTurnStamp <= Manager.Instance.TimeStamp) return;
#if DEBUG
            Console.WriteLine($"AI 正在放置 {PutData.Card} 的卡于位置 {PutData.Pos} ");
            Console.WriteLine($"AI 手牌:{string.Concat(_hands.Select(id => $"{id}\t"))},战场:");
            for (var i = 0; i < _boardSelf.Length; ++i)
            {
                Console.Write(_boardSelf[i] == null ? 0 : _boardSelf[i].Cfg.CardId);
                Console.Write(i             == 2 ? "\n" : "\t");
            }

            Console.WriteLine();
            Console.WriteLine("敌方战场");

            for (var i = 0; i < _boardEnemy.Length; ++i)
            {
                Console.Write(_boardEnemy[i] == null ? 0 : _boardEnemy[i].Cfg.CardId);
                Console.Write(i              == 2 ? "\n" : "\t");
            }

            Console.WriteLine();
#endif

            RestBattleThinking();
            _player.Gate.Send(new PutOnQ {BattleId = _battleId, Pos = PutData.Pos, CardId = PutData.Card});
        }

        // 使用技能(args第一个参数是 appointtargettype)
        private void UseSkill(int pos, UseSKillTime useTime, List<int> args)
        {
            if (_endTurnStamp <= Manager.Instance.TimeStamp) return;

            _player.Gate.Send(new UseSkillQ() {BattleId = _battleId, Pos = pos, UseTime = useTime, Args = args});
        }

        // 主动投降
        private void Surrender(int battleId)
        {
            if (_endTurnStamp <= Manager.Instance.TimeStamp) return;

            _player.Gate.Send(new SurrenderQ {BattleId = battleId});
        }

        // 协议派发
        public void Dispatch(CardActionEnum tag, IUnPackable iUnPackable)
        {
            switch (tag)
            {
                case CardActionEnum.ActionBeginPickP:
                    {
                        var msg = (ActionBeginPickP) iUnPackable;
                        HandleBeginPick(msg);
                    }
                    break;
                case CardActionEnum.ActionPickUpEndP:
                    {
                        var msg = (ActionPickUpEndP) iUnPackable;

                        if (msg.Hand.Count == 0) return;

                        _hands = msg.Hand;
                        _deck  = msg.Deck;
                        break;
                    }
                case CardActionEnum.ActionTurnBeginP: // 这2个消息是给回合开始时动画用的
                    {
                        var msg = (ActionTurnBeginP) iUnPackable;
                        _isSelfTurn = IsDoerSelf(msg.IsOffensive);
                        ++_turnCount;
                    }
                    break;
                case CardActionEnum.ActionTurnBeginEndP:
                    ClientOpEnd();
                    break;
                case CardActionEnum.ActionEnterTurnP:
                    {
                        var msg = (ActionEnterTurnP) iUnPackable;
                        _endTurnStamp = msg.TimeStamp;
                        if (_isSelfTurn)
                        {
                            foreach (var card in _boardSelf)
                            {
                                if (card == null) continue;

                                card.HasSkill = true;
                            }

                            StartBattleThinking();
                        }
                    }
                    break;
                case CardActionEnum.ActionAutoFightP:
                    {
                        _isSelfTurn = false;
                        StopBattleThinking();
                    }
                    break;
                case CardActionEnum.ActionAutoFightEndP:
                    ClientOpEnd();
                    break;
                case CardActionEnum.ActionPickUpSP:
                    {
                        var msg = (ActionPickUpSP) iUnPackable;
                        _hands.AddRange(msg.Picks);
                        Console.WriteLine($"AI 手牌:{string.Concat(_hands.Select(id => $"{id}\t"))}");
                    }
                    break;
                case CardActionEnum.ActionPickUpEP:
                    {
                        var msg = (ActionPickUpEP) iUnPackable;
                        _handsCountEnemy += msg.Number;
                    }
                    break;
                case CardActionEnum.ActionPutOnP:
                    {
                        var msg    = (ActionPutOnP) iUnPackable;
                        var isSelf = IsDoerSelf(msg.IsOffensive);
                        if (isSelf)
                        {
                            _hands.Remove(msg.CardId);
                            Console.WriteLine($"AI 手牌:{string.Concat(_hands.Select(id => $"{id}\t"))}");
                        }
                        else
                            --_handsCountEnemy;

                        var board = GetBoard(msg.IsOffensive);
                        board[msg.Position] = new BoardCard(msg.CardId);
                    }
                    break;
                case CardActionEnum.ActionAttackP: // 放动画用的用不到
                    break;
                case CardActionEnum.ActionPropertyChangeP:
                    {
                        var msg    = (ActionPropertyChangeP) iUnPackable;
                        var target = msg.Target;
                        switch (target.Type)
                        {
                            case ActionTargetType.Card:
                                {
                                    var board = GetBoard(target.IsOffensive);
                                    switch (msg.Type)
                                    {
                                        case ActionPropertyType.Attack:
                                            board[target.Position].Attack += msg.Value;
                                            break;
                                        case ActionPropertyType.Health:
                                            board[target.Position].Health += msg.Value;
                                            break;
                                    }
                                }
                                break;
                            case ActionTargetType.Crystal:
                                {
                                    var crystal = GetCrystal(target.IsOffensive);
                                    crystal[target.Position] += msg.Value;
                                }
                                break;
                        }
                    }
                    break;
                case CardActionEnum.ActionSetPropertyP:
                    {
                        var msg    = (ActionSetPropertyP) iUnPackable;
                        var target = msg.Target;
                        switch (target.Type)
                        {
                            case ActionTargetType.Card:
                                {
                                    var board = GetBoard(target.IsOffensive);
                                    switch (msg.Type)
                                    {
                                        case ActionPropertyType.Attack:
                                            board[target.Position].Attack = msg.Value;
                                            break;
                                        case ActionPropertyType.Health:
                                            board[target.Position].Health = msg.Value;
                                            break;
                                    }
                                }
                                break;
                            case ActionTargetType.Crystal:
                                {
                                    var crystal = GetCrystal(target.IsOffensive);
                                    crystal[target.Position] = msg.Value;
                                }
                                break;
                        }
                    }
                    break;
                case CardActionEnum.ActionDeadP:
                    {
                        var msg   = (ActionDeadP) iUnPackable;
                        var board = GetBoard(msg.IsOffensive);
                        board[msg.Position] = null;
                    }
                    break;
                case CardActionEnum.ActionReviveSP:
                    {
                        var msg = (ActionReviveSP) iUnPackable;
                        _hands.Add(msg.CardId);
                    }
                    break;
                case CardActionEnum.ActionReviveEP:
                    ++_handsCountEnemy;
                    break;
                case CardActionEnum.ActionBackP:
                    {
                        var msg   = (ActionBackP) iUnPackable;
                        var board = GetBoard(msg.IsOffensive);
                        _player.Card.Deck.Add(board[msg.Position].Cfg.CardId);
                        board[msg.Position] = null;
                    }
                    break;
                case CardActionEnum.ActionMoveP:
                    {
                        var msg   = (ActionMoveP) iUnPackable;
                        var board = GetBoard(msg.IsOffensive);
                        board[msg.NewPos]     = board[msg.CurrentPos];
                        board[msg.CurrentPos] = null;
                    }
                    break;
                case CardActionEnum.ActionModifyStaminaLimitP: // 显示用的
                    break;
                case CardActionEnum.ActionModifyStaminaP:
                    {
                        var msg = (ActionModifyStaminaP) iUnPackable;
                        if (IsDoerSelf(msg.IsOffensive))
                            _apNumSelf = msg.Value;
                        else
                            _apNumEnemy = msg.Value;
                    }
                    break;
                case CardActionEnum.ActionUseSkillP: // 显示用的
                    break;
                case CardActionEnum.ActionAddBuffP:
                    {
                        var msg = (ActionAddBuffP) iUnPackable;
                        if (msg.Buff.Type == ActionBuffType.Card)
                        {
                            var board = GetBoard(msg.Buff.IsOffensive);
                            board[msg.Buff.Position].IsMarked = true;
                        }
                        else
                        {
                            // TODO 位置buff
                        }
                    }
                    break;
                case CardActionEnum.ActionTriggerBuffP: // 显示用的
                    break;
                case CardActionEnum.ActionRmvBuffP:
                    {
                        var msg = (ActionRmvBuffP) iUnPackable;
                        if (msg.Buff.Type == ActionBuffType.Card)
                        {
                            var board = GetBoard(msg.Buff.IsOffensive);
                            board[msg.Buff.Position].IsMarked = false;
                        }
                        else
                        {
                            // TODO 位置buff
                        }
                    }
                    break;
            }
        }

        // 异步获得牌组信息,然后选择手牌
        private async void HandleBeginPick(ActionBeginPickP msg)
        {
            if (_hands == null)
            {
                var card = await _player.Card.GetCardInfoAsync();
                card.Deck.Shuffle();
                _hands = card.Deck.GetRange(0, _handFillUpNum);
                _deck  = card.Deck.GetRange(_handFillUpNum, card.Deck.Count - _handFillUpNum);
            }

            PickEnd(_hands, _deck);
        }
    }
}